/**
 * 交叉类型 (并集 “&”)
 */
const mergeFunc = <T, U>(arg1: T, arg2: U): T & U => {
    let res = {} as T & U
    res = Object.assign(arg1, arg2)
    return res
}
console.log(mergeFunc({ a: 'a' }, { b: 'b' })); // { a: "a", b: "b" }

/**
 * 联合类型 (交集 “|”)
 */
const getLengthFunc = (content: string | number): number => {
    if (typeof content === 'string') {
        return content.length
    } else {
        return content.toString().length
    }
}
console.log(getLengthFunc("false"));

/**
 * 类型保护
 */

const valueList = [123, "abc"]

// 下列函数返回string或number 类型
const getRandomValue = () => {
    const num = Math.random() * 10
    if (num < 5) {
        return valueList[0]
    } else {
        return valueList[1]
    }
}
let item = getRandomValue()
console.log(item);
// item 类型为 string|number 导致报错
if ((item as string).length) {
    // console.log(item.length); // 报错
} else {
    // console.log(item.toFixed()); // 报错
}

/**
 * 类型保护
 */
(() => {
    function isString(value: number | string): value is string {
        return typeof value === "string"
    }
    if (isString(item)) {
        console.log(item.length); // 报错
    } else {
        console.log(item.toFixed()); // 报错
    }

    // typeof // 只能用于等或不等判断
    // 类型只能是 string/number/boolean/symbol 才能有类型保护的效果
    if (typeof item === "string") {
        console.log(item.length);
    } else {
        console.log(item.toFixed());
    }
})()

// instanceof // 的类型保护
class CreatedByClass1 {
    public age = 18
    constructor() { }
}
class CreatedByClass2 {
    public name = "username"
    constructor() { }
}
function getRandomItem() {
    return Math.random() < 0.5 ? new CreatedByClass1() : new CreatedByClass2();
}
(() => {
    const item2 = getRandomItem()
    if (item2 instanceof CreatedByClass1) {
        console.log(item2.age);
    } else {
        console.log(item2.name);
    }
})()

/**
 * unll undefined
 * 严格模式下undefined和 unll是其它类型的子类型，配置中可以开关
 */
const sunFunc = (x: number, y?: number) => {
    return x + (y || 0) // 注意此处y为联合类型number | undefined
}


const getLengthFunction = (value: string | null): number => {
    return value === null ? 0 : (value || "").length
}



function getSplicedStr(num: number | null): string {
    function getRes(prefix: string) {
        return prefix + num!.toFixed() // 感叹号是非null和非undefined的类型断言
    }
    num = num || 1
    console.log(num);

    return getRes("a-")
}
console.log(getSplicedStr(NaN));

/**
 * 类型别名
 */
type TypeString = string
let str2: TypeString
str2 = "33"

// 泛型类型别名
type PositionType<T> = { x: T, y: T }
const position1: PositionType<number> = {
    x: 1,
    y: -1
}
const position2: PositionType<string> = {
    x: "left",
    y: "top"
}

// 递归类型别名
type Childs<T> = {
    current: T,
    child?: Childs<T>
}
const ccc: Childs<string> = {
    current: "first",
    child: {
        current: 'second',
        child: {
            current: "third"
        }
    }
}

//  type Childs = Childs[] // 错误用法
// 类型别名不能被 extends | implements

type Alias = {
    num: number
}

interface Interface {
    num: number
}
let _alias: Alias = {
    num: 123
}
let _interface: Interface = {
    num: 456
}
_alias = _interface // 类型兼容

/**
 * 字符串字面量类型别名
 */

type Name = "Xname"
const namex: Name = "Xname" // 必须是指定的字符串值

type UniName = "name1" | "name2" | "name3"
const uniName: UniName = "name3"

type XNumber = 1 | 2
const xName1: XNumber = 1
const xName2: XNumber = 2
// const xName3: XNumber = 3 // 报错

/**
 * 可辨识联合 （标签联合||代数数据联合）
 * 两要素
 * 1.具有普通的单例类型属性
 * 2.一个类型别名包含了哪些类型的联合
 */
interface Square {
    kind: 'square'
    size: number
}
interface Rectangle {
    kind: "rectangle"
    height: number
    width: number
}
interface Circle {
    kind: "circle"
    radius: number
}
type Shape = Square | Rectangle | Circle
function getArea(s: Shape): number {
    switch (s.kind) {
        case "square":
            return s.size * s.size
        case "rectangle":
            return s.height * s.width
        case "circle":
            return Math.PI * s.radius ** 2 // ES7 次方
    }
}

/**
 * 完整性检查
 */
function assertNever(value: never): never {
    throw new Error("未知错误" + value);
}

function getArea2(s: Shape): number {
    switch (s.kind) {
        case "square":
            return s.size * s.size
        case "rectangle":
            return s.height * s.width
        case "circle":
            return Math.PI * s.radius ** 2 // ES7 次方
        default:
            assertNever(s) // 完整性检查
    }
}